//! TOML 配置解析器
//!
//! 负责从 TOML 字符串解析配置。
//!
//! ## 主要函数
//!
//! - [`parse_config`] - 从 TOML 字符串解析配置
//!
//! ## 示例
//!
//! ```rust
//! use log4r::config::parse_config;
//!
//! let toml = r#"
//! [log4r]
//! level = "debug"
//! app_name = "my-app"
//! "#;
//!
//! let config = parse_config(toml).expect("解析应成功");
//! assert_eq!(config.app_name, "my-app");
//! ```

use serde::Deserialize;

use crate::config::types::Log4rConfig;
use crate::error::Log4rError;

/// TOML 配置文件包装结构体
///
/// 用于支持 `[log4r]` 顶级配置节
#[derive(Debug, Deserialize)]
struct ConfigWrapper {
    log4r: Log4rConfig,
}

/// 从 TOML 字符串解析配置
///
/// 支持两种格式：
/// 1. 带 `[log4r]` 前缀的配置
/// 2. 直接配置（无前缀）
///
/// # Arguments
///
/// * `content` - TOML 格式的配置字符串
///
/// # Returns
///
/// * `Ok(Log4rConfig)` - 解析成功的配置
/// * `Err(Log4rError::ConfigError)` - 解析失败，包含行号和错误描述
///
/// # Example
///
/// ```rust
/// use log4r::config::parse_config;
/// use log4r::Level;
///
/// // 带 [log4r] 前缀
/// let toml = r#"
/// [log4r]
/// level = "debug"
/// app_name = "my-app"
/// "#;
/// let config = parse_config(toml).expect("解析应成功");
/// assert_eq!(config.level, Level::Debug);
/// assert_eq!(config.app_name, "my-app");
///
/// // 无前缀直接解析
/// let toml = r#"
/// level = "info"
/// "#;
/// let config = parse_config(toml).expect("解析应成功");
/// assert_eq!(config.level, Level::Info);
///
/// // 空配置返回默认值
/// let config = parse_config("").expect("空配置应返回默认值");
/// assert_eq!(config.level, Level::Info);
/// ```
pub fn parse_config(content: &str) -> Result<Log4rConfig, Log4rError> {
    // 处理空内容或仅注释
    if is_empty_or_comments_only(content) {
        return Ok(Log4rConfig::default());
    }

    // 尝试解析带 [log4r] 前缀的配置
    if content.contains("[log4r]") {
        toml::from_str::<ConfigWrapper>(content)
            .map(|w| w.log4r)
            .map_err(format_toml_error)
    } else {
        // 直接解析为 Log4rConfig
        toml::from_str::<Log4rConfig>(content).map_err(format_toml_error)
    }
}

/// 检查内容是否为空或仅包含注释
fn is_empty_or_comments_only(content: &str) -> bool {
    let trimmed = content.trim();
    if trimmed.is_empty() {
        return true;
    }

    // 检查每一行是否都是空行或注释
    trimmed.lines().all(|line| {
        let line = line.trim();
        line.is_empty() || line.starts_with('#')
    })
}

/// 格式化 TOML 解析错误为友好的中文信息
///
/// 将 toml 库的错误转换为包含行号的友好错误信息。
fn format_toml_error(err: toml::de::Error) -> Log4rError {
    // toml 0.8+ 的 Error 直接包含行列信息
    let msg = err.to_string();
    Log4rError::ConfigError(format!("配置解析失败: {}", msg))
}

/// 从 TOML 内容和字节位置计算行号
///
/// # Arguments
///
/// * `content` - TOML 配置内容
/// * `byte_pos` - 字节位置
///
/// # Returns
///
/// 行号（从 1 开始）
#[allow(dead_code)]
fn calculate_line_number(content: &str, byte_pos: usize) -> usize {
    content[..byte_pos.min(content.len())]
        .chars()
        .filter(|&c| c == '\n')
        .count()
        + 1
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
    use super::*;
    use crate::config::{ConsoleTarget, TimeZone};
    use crate::Level;

    // ========================================================================
    // AC-3.2.1: 成功解析有效的 TOML 配置字符串
    // ========================================================================

    /// GIVEN: 完整的 TOML 配置
    /// WHEN: 调用 parse_config
    /// THEN: 返回 Ok(Log4rConfig) 包含所有字段
    #[test]
    fn test_parse_full_config() {
        let toml = r#"
[log4r]
level = "debug"
app_name = "test-app"

[log4r.modules]
"my_app::db" = "trace"
"my_app::http" = "warn"

[log4r.appenders.console]
enabled = true
target = "stderr"
level = "info"

[log4r.appenders.file]
enabled = true
path = "logs/app.log"
level = "debug"

[log4r.format]
pattern = "{message}"
time_format = "[hour]:[minute]"
time_zone = "utc"

[log4r.async]
enabled = false
queue_size = 50000
"#;

        let config = parse_config(toml).expect("解析应成功");

        assert_eq!(config.level, Level::Debug);
        assert_eq!(config.app_name, "test-app");
        assert_eq!(config.modules.get("my_app::db"), Some(&Level::Trace));
        assert_eq!(config.modules.get("my_app::http"), Some(&Level::Warn));

        let console = config.appenders.console.expect("console 应存在");
        assert!(console.enabled);
        assert_eq!(console.target, ConsoleTarget::Stderr);
        assert_eq!(console.level, Some(Level::Info));

        let file = config.appenders.file.expect("file 应存在");
        assert!(file.enabled);
        assert_eq!(file.path, "logs/app.log");
        assert_eq!(file.level, Some(Level::Debug));

        assert_eq!(config.format.pattern, "{message}");
        assert_eq!(config.format.time_format, "[hour]:[minute]");
        assert_eq!(config.format.time_zone, TimeZone::Utc);

        assert!(!config.async_config.enabled);
        assert_eq!(config.async_config.queue_size, 50000);
    }

    // ========================================================================
    // AC-3.2.2: 支持 [log4r] 顶级配置节解析
    // ========================================================================

    /// GIVEN: 带 [log4r] 前缀的配置
    /// WHEN: 调用 parse_config
    /// THEN: 正确解析配置
    #[test]
    fn test_parse_with_log4r_prefix() {
        let toml = r#"
[log4r]
level = "warn"
app_name = "prefixed-app"
"#;

        let config = parse_config(toml).expect("解析应成功");
        assert_eq!(config.level, Level::Warn);
        assert_eq!(config.app_name, "prefixed-app");
    }

    /// GIVEN: 无 [log4r] 前缀的配置
    /// WHEN: 调用 parse_config
    /// THEN: 直接解析为 Log4rConfig
    #[test]
    fn test_parse_without_log4r_prefix() {
        let toml = r#"
level = "error"
app_name = "direct-app"
"#;

        let config = parse_config(toml).expect("解析应成功");
        assert_eq!(config.level, Level::Error);
        assert_eq!(config.app_name, "direct-app");
    }

    // ========================================================================
    // AC-3.2.3: 支持嵌套配置节解析
    // ========================================================================

    /// GIVEN: 包含嵌套配置节的 TOML
    /// WHEN: 调用 parse_config
    /// THEN: 正确解析所有嵌套配置
    #[test]
    fn test_parse_nested_sections() {
        let toml = r#"
[log4r]
level = "info"

[log4r.appenders.console]
enabled = true
target = "stdout"

[log4r.appenders.file]
enabled = false
path = "test.log"

[log4r.format]
pattern = "custom"

[log4r.async]
queue_size = 5000

[log4r.modules]
"test::module" = "debug"
"#;

        let config = parse_config(toml).expect("解析应成功");

        assert!(config.appenders.console.is_some());
        assert!(config.appenders.file.is_some());
        assert_eq!(config.format.pattern, "custom");
        assert_eq!(config.async_config.queue_size, 5000);
        assert_eq!(config.modules.get("test::module"), Some(&Level::Debug));
    }

    // ========================================================================
    // AC-3.2.4: TOML 语法错误时返回 ConfigError，包含行号
    // ========================================================================

    /// GIVEN: TOML 语法错误（缺少闭合括号）
    /// WHEN: 调用 parse_config
    /// THEN: 返回 Err(ConfigError) 包含位置信息
    #[test]
    fn test_parse_syntax_error_missing_bracket() {
        let toml = r#"
[log4r
level = "info"
"#;

        let result = parse_config(toml);
        assert!(result.is_err());

        let err = result.unwrap_err().to_string();
        assert!(err.contains("配置"), "错误信息应包含'配置'");
    }

    /// GIVEN: TOML 语法错误（缺少引号）
    /// WHEN: 调用 parse_config
    /// THEN: 返回 Err(ConfigError)
    #[test]
    fn test_parse_syntax_error_missing_quote() {
        let toml = r#"
[log4r]
level = info
"#;

        let result = parse_config(toml);
        assert!(result.is_err());

        let err = result.unwrap_err().to_string();
        assert!(err.contains("配置解析失败"));
    }

    // ========================================================================
    // AC-3.2.5: 字段类型错误时返回 ConfigError，包含字段名
    // ========================================================================

    /// GIVEN: 字段类型错误（数字赋给字符串字段）
    /// WHEN: 调用 parse_config
    /// THEN: 返回 Err(ConfigError)
    #[test]
    fn test_parse_type_error() {
        let toml = r#"
[log4r]
app_name = 123
"#;

        let result = parse_config(toml);
        assert!(result.is_err());

        let err = result.unwrap_err().to_string();
        assert!(err.contains("配置解析失败"));
    }

    /// GIVEN: queue_size 使用字符串而非数字
    /// WHEN: 调用 parse_config
    /// THEN: 返回 Err(ConfigError)
    #[test]
    fn test_parse_type_error_queue_size() {
        let toml = r#"
[log4r]
level = "info"

[log4r.async]
queue_size = "not a number"
"#;

        let result = parse_config(toml);
        assert!(result.is_err());
    }

    // ========================================================================
    // AC-3.2.6: 无效日志级别名称时返回 ConfigError
    // ========================================================================

    /// GIVEN: 无效的日志级别名称
    /// WHEN: 调用 parse_config
    /// THEN: 返回 Err(ConfigError)
    #[test]
    fn test_parse_invalid_level() {
        let toml = r#"
[log4r]
level = "invalid"
"#;

        let result = parse_config(toml);
        assert!(result.is_err());

        let err = result.unwrap_err().to_string();
        assert!(err.contains("配置解析失败"));
    }

    /// GIVEN: 模块级别使用无效名称
    /// WHEN: 调用 parse_config
    /// THEN: 返回 Err(ConfigError)
    #[test]
    fn test_parse_invalid_module_level() {
        let toml = r#"
[log4r]
level = "info"

[log4r.modules]
"my_app" = "not_a_level"
"#;

        let result = parse_config(toml);
        assert!(result.is_err());
    }

    // ========================================================================
    // AC-3.2.7: 支持大小写不敏感的级别名称解析
    // ========================================================================

    /// GIVEN: 不同大小写的级别名称
    /// WHEN: 调用 parse_config
    /// THEN: 都能正确解析
    #[test]
    fn test_parse_level_case_insensitive() {
        let test_cases = vec![
            ("INFO", Level::Info),
            ("info", Level::Info),
            ("Info", Level::Info),
            ("iNfO", Level::Info),
            ("DEBUG", Level::Debug),
            ("debug", Level::Debug),
            ("TRACE", Level::Trace),
            ("WARN", Level::Warn),
            ("ERROR", Level::Error),
            ("FATAL", Level::Fatal),
        ];

        for (level_str, expected) in test_cases {
            let toml = format!(
                r#"
[log4r]
level = "{}"
"#,
                level_str
            );

            let config =
                parse_config(&toml).unwrap_or_else(|_| panic!("应解析级别: {}", level_str));
            assert_eq!(config.level, expected, "级别 {} 解析失败", level_str);
        }
    }

    // ========================================================================
    // AC-3.2.8: 缺失的配置项使用默认值
    // ========================================================================

    /// GIVEN: 最小配置（仅 level）
    /// WHEN: 调用 parse_config
    /// THEN: 其他字段使用默认值
    #[test]
    fn test_parse_minimal_config() {
        let toml = r#"
[log4r]
level = "debug"
"#;

        let config = parse_config(toml).expect("解析应成功");

        assert_eq!(config.level, Level::Debug);
        assert_eq!(config.app_name, "log4r"); // 默认值
        assert!(config.modules.is_empty()); // 默认值
        assert!(config.appenders.console.is_none()); // 默认值
        assert!(config.async_config.enabled); // 默认值
        assert_eq!(config.async_config.queue_size, 10000); // 默认值
    }

    /// GIVEN: 部分配置（缺少某些字段）
    /// WHEN: 调用 parse_config
    /// THEN: 缺失字段使用默认值
    #[test]
    fn test_parse_partial_config() {
        let toml = r#"
[log4r]
app_name = "partial-app"

[log4r.appenders.console]
target = "stderr"
"#;

        let config = parse_config(toml).expect("解析应成功");

        assert_eq!(config.level, Level::Info); // 默认值
        assert_eq!(config.app_name, "partial-app");

        let console = config.appenders.console.expect("console 应存在");
        assert!(console.enabled); // 默认值
        assert_eq!(console.target, ConsoleTarget::Stderr);
        assert!(console.level.is_none()); // 默认值
    }

    // ========================================================================
    // AC-3.2.9: 空配置字符串或仅包含注释的配置使用默认值
    // ========================================================================

    /// GIVEN: 空字符串
    /// WHEN: 调用 parse_config
    /// THEN: 返回默认配置
    #[test]
    fn test_parse_empty_config() {
        let config = parse_config("").expect("空配置应返回默认值");

        assert_eq!(config.level, Level::Info);
        assert_eq!(config.app_name, "log4r");
    }

    /// GIVEN: 仅包含空白字符
    /// WHEN: 调用 parse_config
    /// THEN: 返回默认配置
    #[test]
    fn test_parse_whitespace_only() {
        let config = parse_config("   \n\t\n   ").expect("空白配置应返回默认值");

        assert_eq!(config.level, Level::Info);
    }

    /// GIVEN: 仅包含注释
    /// WHEN: 调用 parse_config
    /// THEN: 返回默认配置
    #[test]
    fn test_parse_comments_only() {
        let toml = r#"
# 这是注释
# 另一行注释

# 更多注释
"#;

        let config = parse_config(toml).expect("仅注释应返回默认值");

        assert_eq!(config.level, Level::Info);
        assert_eq!(config.app_name, "log4r");
    }

    /// GIVEN: 注释和空行混合
    /// WHEN: 调用 parse_config
    /// THEN: 返回默认配置
    #[test]
    fn test_parse_comments_and_empty_lines() {
        let toml = r#"
# 配置文件注释

# 第一部分

# 第二部分

"#;

        let config = parse_config(toml).expect("注释和空行应返回默认值");
        assert_eq!(config.level, Level::Info);
    }

    // ========================================================================
    // 辅助函数测试
    // ========================================================================

    #[test]
    fn test_is_empty_or_comments_only() {
        assert!(is_empty_or_comments_only(""));
        assert!(is_empty_or_comments_only("   "));
        assert!(is_empty_or_comments_only("\n\n"));
        assert!(is_empty_or_comments_only("# comment"));
        assert!(is_empty_or_comments_only("# comment\n# another"));
        assert!(is_empty_or_comments_only("  # comment with spaces"));

        assert!(!is_empty_or_comments_only("level = \"info\""));
        assert!(!is_empty_or_comments_only("[log4r]"));
        assert!(!is_empty_or_comments_only("# comment\nlevel = \"info\""));
    }
}
